'use strict'

const http = require('http')
const fs = require('fs');
const { info } = require('console');

const fsp=fs.promises;

function parseBodyHeaders(data) {
    let headers = {}
    let dsplit = data.split('\r\n').filter(p => p.length > 0)

    let ind
    let k
    for (let d of dsplit) {
        ind = d.indexOf(':')

        if (ind <= 0) continue

        k = d.substring(0, ind).toLowerCase()

        headers[k] = d.substring(ind + 1).trim()

    }

    let cpd = headers['content-disposition']

    //? 按照; 分割成数组，第一个元素form-data去掉
    let clist = cpd.split('; ').slice(1)

    let name, filename

    for (let a of clist) {
        ind = a.indexOf('filename="')
        if (ind >= 0) {
            filename = a.substring(ind + 10, a.length - 1)
        } else {
            name = a.substring(6, a.length - 1)
        }
    }

    return {
        headers,
        name,
        filename
    }

}

/**
 * 
 * @param {Buffer} bodyData 原始的Body数据
 * @param {Object} reqHeaders reuqest.headers
 */

function parseBody(bodyData, reqHeaders) {

    //content-type: multipart/form-data; boundary=-----34294329
    let ctype = reqHeaders['content-type']

    //拿到分界线boundary
    let bdy = ctype.substring(ctype.indexOf('=') + 1)

    //数据中用于分割文件的分界线
    //let bdy = `--${bdy}\r\n`

    let crlf_bdy = `\r\n--${bdy}`

    //从偏移bdy的长度开始查找下一个分界线的位置
    let data_end_index = bodyData.indexOf(crlf_bdy, crlf_bdy.length)

    //body消息数据中header部分的最后索引
    let header_end_index = bodyData.indexOf('\r\n\r\n', crlf_bdy.length)

    let header_data = bodyData.toString('utf8',
        crlf_bdy.length,
        header_end_index);

    //解析body数据中的消息头
    let hd = parseBodyHeaders(header_data)

    let fileinfo = {
        start: header_end_index + 4,
        end: data_end_index,
    }

    fileinfo.length = fileinfo.end - fileinfo.start

    return {
        name: hd.name,
        filename: hd.filename,
        headers: hd.headers,
        start: fileinfo.start,
        end: fileinfo.end,
        length: fileinfo.length
    }

}

function parseExtName(filename){
    if(filename.length<2) return ''

    let namesplit=filename.split('.').filter(p=>p.length>0)
    //? 没有后缀名
    if(namesplit.length<2) return ''

    return `.${namesplit[namesplit.length-1]}`
}

let routerTable = {

    GET: {
        '/upload': async (ctx) => {
            let stm = fs.createReadStream('./upload.html');
            stm.pipe(ctx.res);
            stm.on('end', () => {
                ctx.end();
            })
        }
    },

    POST: {
        '/upload': async (ctx) => {
            console.log(ctx.files);

            let finfo=ctx.files;

            let filename=`${Date.now()}-${parseInt(Math.random()*10000)+10000}`+parseExtName(finfo.filename);
            //? 打开一个没有的文件
            
            let fh=await fsp.open(filename,'w+');
            let fret=await fh.write(ctx.rawBody,finfo.start,finfo.length,0);
            //? 关闭打开的文件
            fh.close();
            console.log(fret);
            //? 返回写入的字节数  end传入的必须是string或者buffer类型，不能是其它类型
            ctx.end(`${fret.bytesWritten}`);
        }
    }

}
/**------------------------START SEVER------------------------------------- */
let serv = http.createServer()

serv.on('request', (request, response) => {
    let rm = routerTable[request.method]
    //! 解决带查询字符串问题
    //? 根据?切割字符串
    let usplit=request.url.split('?');
    let pathname=usplit[0];
    //! 问号后面没有东西，usplit[1]为undefined返回空字符
    let querystring=usplit[1] || ''

                //? 不带查询字符串的 
    // if (!rm || !rm[request.url]) {
    //     response.statusCode = 404
    //     response.end('page not found')
    //     return
    // }
    if (!rm || !rm[pathname]) {
        response.statusCode = 404;
        response.end('page not found');
        return
    }
    
    //! ctx is context , 创建请求上下文对象
    let ctx={
        req:request,
        res:response,
        method:request.method,
        headers:request.headers,
        end:(data,encoding='utf8')=>{
            response.end(data,encoding)
        },
        write:(data,encoding='utf8')=>{
            response.write(data,encoding)
        },
        path:pathname,
        querystring:querystring,
        rawBody:null

    }
    //? 允许提交body数据的POST PUT PATCH DELETE
    //? PD
    //? 不允许提交body数据的GET HEAD TRACE OPTIONS
    let bufferList=[];
    let totalLength=0;
    if('PD'.indexOf(request.method[0])>=0){
        request.on('data',chunk=>{
            totalLength+=chunk.length;
            bufferList.push(chunk);
        })
    }else{
        request.on('data',chunk=>{
            response.statusCode=400;
            response.end();
        })
    }
    request.on('end',()=>{
        if(bufferList.length>0){
            ctx.rawBody=Buffer.concat(bufferList,totalLength);
            bufferList=[];
        }
        
        //! 上传文件，parsebody自动执行
        let ctype=ctx.headers['content-type']||'';
        if(ctx.rawBody && ctype.indexOf('multipart/form-data')===0){
            ctx.files=parseBody(ctx.rawBody,ctx.headers);
        }

        rm[pathname](ctx);
    })
    
})

serv.listen(3456)

